// *** LICENSE HEADER ***
// Filename: ./fonts.cpp
// This file is part of Nodewarz 0.1
// Nodewarz is a 3d OpenGL strategy game
// Copyright (C) 2007 CZestmyr
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *** END LICENSE HEADER ***
#include "fonts.h"

fonts::fonts(engine* newParent) {
    parent = newParent;
}

void fonts::LoadFonts() {
    FT_Library library;
    FT_Face face;
    int error, glyphWidth, glyphHeight;
    GLubyte* textureData;

    parent->GetLogger()->log(LL_INFORMATION, "Trying to load fonts");

    //Initialize Freetype
    error = FT_Init_FreeType(&library);
    if (error) {
        parent->GetLogger()->log(LL_ERROR, "Could not initialize FreeType library. Check your installation");
        return;
    }

    //Begin generating a font
    for (int fontNum = 0; fontNum < 2; ++fontNum){

    //Load font file
    switch (fontNum){
        case FT_GAME:
            error = FT_New_Face(library, "./neustyle_bold.ttf", 0, &face);
        break;
        case FT_MENU:
            error = FT_New_Face(library, "./arena_condensed_bold.ttf", 0, &face);
        break;
    }

    if (error == FT_Err_Unknown_File_Format) {
        parent->GetLogger()->log(LL_ERROR, "Could not load font: unknown file format");
        FT_Done_FreeType(library);
        return;
    } else if (error) {
        parent->GetLogger()->log(LL_ERROR, "Could not load font file");
        FT_Done_FreeType(library);
        return;
    }

    //Set font size
    error = FT_Set_Pixel_Sizes(face, 64, 64);
    if (error) {
        parent->GetLogger()->log(LL_ERROR, "Could not change font size");
        FT_Done_Face(face);
        FT_Done_FreeType(library);
        return;
    }

    //Generate textures for the glyphs
    glGenTextures(256, &fontGlyphs[fontNum][0][0]);

    bool success = true;

    //Allocate texture space for the glyph texture
    textureData = (GLubyte*) malloc(sizeof(GLubyte) * 2 * 64 * 64);
    //Load all the basic 256 ascii characters
    for (int i = 0; i < 256; ++i) {
        //Load the freetype glyph
        FT_Load_Glyph(face, FT_Get_Char_Index(face, i), 0);
        //Save the glyph metrics
        fontGlyphs[fontNum][2][i] = 64 - (face->glyph->metrics.horiBearingY >> 6);
        //Convert glyph to bitmap if it isn't bitmap yet
        if (face->glyph->format != FT_GLYPH_FORMAT_BITMAP) {
            error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
            if (error) {
                success = false;
                continue;
            }
        }
        //Gt the glyph dimensions and copy the bitmap buffer onto the openGL texture buffer
        glyphWidth = face->glyph->bitmap.width;
        glyphHeight = face->glyph->bitmap.rows;
        fontGlyphs[fontNum][1][i] = face->glyph->advance.x >> 6;
        for (int y = 0; y < 64; ++y) {
            for (int x = 0; x < 64; ++x) {
                textureData[2 *(y*64+x)] = 255;
                textureData[2 *(y*64+x) + 1] =
                    (y < glyphHeight && x < glyphWidth) ?
                    face->glyph->bitmap.buffer[y*glyphWidth + x] :
                    0;
            }
        }
        //Generate the openGL texture
        glBindTexture(GL_TEXTURE_2D, fontGlyphs[fontNum][0][i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _TEXTURE_FILTER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _TEXTURE_FILTER);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 64, 64, 0,
                     GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, textureData);
    }
    //Free the allocated memory
    free(textureData);

    if (!success)
        parent->GetLogger()->log(LL_ERROR, "Could not rasterize a glyph");

    FT_Done_Face(face);

    } //End generating a font

    //Deinit freetype
    FT_Done_FreeType(library);
}

void fonts::UnloadFonts() {}

void fonts::Print(fontType type, const char* text, int posX, int posY, int height, SDL_Color color) {
    //Size coefficient that multiplies the glyph dimensions
    float sizeCoeff = height / 64.0f;
    //Offset of the glyph from top of the line
    float topOffset;
    //Distance travelled when the glyph is drawn
    float advance;
    //Length of the line. It is saved for the newlines to be able to jump at the beginning
    float lineLength = 0.0f;

    //Set 2d projection matrix
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0, _WIDTH, 0, _HEIGHT, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glEnable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glColor3ub(color.r, color.g, color.b);
    glTranslatef(posX, posY, 0);
    for (; *text != 0; ++text) {
        //Calculate the advance of the glyph
        advance = fontGlyphs[type][1][*text] * sizeCoeff;
        glBindTexture(GL_TEXTURE_2D, fontGlyphs[type][0][*text]);
        //Calculate the glyph's top offset
        topOffset = fontGlyphs[type][2][*text] * sizeCoeff;
        //Make a new line instead of the character '#'
        if (*text == '#') {
            glTranslatef(-lineLength, -height * 1.3, 0);
            lineLength = 0.0f;
        } else {
            glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 1.0f);
            glVertex2i(0, (int)-topOffset);
            glTexCoord2f(1.0f, 1.0f);
            glVertex2i(height, (int)-topOffset);
            glTexCoord2f(1.0f, 0.0f);
            glVertex2i(height, (int)(height - topOffset));
            glTexCoord2f(0.0f, 0.0f);
            glVertex2i(0, (int)(height - topOffset));
            glEnd();
            lineLength += advance;
            glTranslatef(advance, 0, 0);
        }
    }
    glEnable(GL_DEPTH_TEST);

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
}
